package com.demo.common.utils;


import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;

import java.sql.Date;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;

/**
 * description:
 */
@Log4j2
public class CalendarUtil {

    /**
     * new a Calendar instance
     */
    static GregorianCalendar cldr = new GregorianCalendar();

    /**
     * the milli second of a day
     */
    public static final long DAYMILLI = 24 * 60 * 60 * 1000;

    /**
     * the milli seconds of an hour
     */
    public static final long HOURMILLI = 60 * 60 * 1000;

    /**
     * the milli seconds of a minute
     */
    public static final long MINUTEMILLI = 60 * 1000;

    /**
     * the milli seconds of a second
     */
    public static final long SECONDMILLI = 1000;

    /**
     * added time
     */
    public static final String TIMETO = " 23:59:59";


    /**
     * 零点时间
     */
    public static final String ZEROTIEM = "00:00:00";

    /**
     * set the default time zone
     */
    static {
        cldr.setTimeZone(java.util.TimeZone.getTimeZone("GMT+9:00"));
    }

    /**
     * flag before
     */
    public static final transient int BEFORE = 1;

    /**
     * flag after
     */
    public static final transient int AFTER = 2;

    /**
     * flag equal
     */
    public static final transient int EQUAL = 3;

    /**
     * date format dd/MMM/yyyy:HH:mm:ss +0900
     */
    public static final String TIME_PATTERN_LONG = "dd/MMM/yyyy:HH:mm:ss +0900";

    /**
     * date format dd/MM/yyyy:HH:mm:ss +0900
     */
    public static final String TIME_PATTERN_LONG2 = "dd/MM/yyyy:HH:mm:ss +0900";

    /**
     * date format yyyy-MM-dd HH:mm:ss
     */
    public static final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * date format YYYY-MM-DD HH24:MI:SS
     */
    public static final String DB_TIME_PATTERN = "YYYY-MM-DD HH24:MI:SS";

    /**
     * date format dd/MM/yy HH:mm:ss
     */
    public static final String TIME_PATTERN_SHORT = "dd/MM/yy HH:mm:ss";

    /**
     * date format dd/MM/yy HH24:mm
     */
    public static final String TIME_PATTERN_SHORT_1 = "yyyy/MM/dd HH:mm";

    /**
     * date format yyyyMMddHHmmss
     */
    public static final String TIME_PATTERN_SESSION = "yyyyMMddHHmmss";

    /**
     * date format yyyyMMdd
     */
    public static final String DATE_FMT_0 = "yyyyMMdd";

    /**
     * date format yyyy/MM/dd
     */
    public static final String DATE_FMT_1 = "yyyy/MM/dd";

    /**
     * date format yyyy/MM/dd hh:mm:ss
     */
    public static final String DATE_FMT_2 = "yyyy/MM/dd hh:mm:ss";

    /**
     * date format yyyy-MM-dd
     */
    public static final String DATE_FMT_3 = "yyyy-MM-dd";

    public static final String DATE_FMT_4 = "yyyy-MM-dd HH:mm";

    public static final String DATE_FMT_5 = "yyyy-MM";

    public static final String DATE_FMT_6 = "yyyyMM";

    /** date format */
//    public static final Timestamp DATE_FROM = Timestamp.valueOf("1970-01-01 00:00:00.000000000");

    /** date to */
//    public static final Timestamp DATE_TO = Timestamp.valueOf("2015-01-01 00:00:00.000000000");

    /**
     * change string to date
     *
     * @param sDate the date string
     * @param sFmt  the date format
     * @return Date object
     */
    public static java.util.Date toDate(String sDate, String sFmt) {
        SimpleDateFormat sdfFrom = null;
        java.util.Date dt = null;

        try {
            sdfFrom = new SimpleDateFormat(sFmt);
            dt = sdfFrom.parse(sDate);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            sdfFrom = null;
        }

        return dt;
    }

    public static java.util.Date stringToDate(String sDate, String sFmt) {
        if (StringUtils.isBlank(sFmt)) {
            sFmt = "yyyy-MM-dd hh:mm:ss";
        }
        SimpleDateFormat sf = new SimpleDateFormat(sFmt);
        ParsePosition pos = new ParsePosition(0);
        java.util.Date parse = sf.parse(sDate, pos);
        return parse;
    }

    /**
     * change date to string
     *
     * @param dt a date
     * @return the format string
     */
    public static String toString(java.util.Date dt) {
        return toString(dt, DATE_FMT_0);
    }

    /**
     * change date object to string
     *
     * @param dt   date object
     * @param sFmt the date format
     * @return the formatted string
     */
    public static String toString(java.util.Date dt, String sFmt) {
        SimpleDateFormat sdfFrom = null;
        String sRet = null;

        try {
            sdfFrom = new SimpleDateFormat(sFmt);
            sRet = sdfFrom.format(dt).toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            sdfFrom = null;
        }

        return sRet;
    }

    /**
     * get the interval days between two date
     *
     * @param dt1 date 1
     * @param dt2 date 2
     * @return the interval days
     */
    public static int getIntervalDays(java.util.Date dt1, java.util.Date dt2) {
        long intervalMilli = dt2.getTime() - dt1.getTime();

        return (int) (intervalMilli / (24 * 60 * 60 * 1000));
    }

    /**
     * format the date in given pattern
     *
     * @param d       date
     * @param pattern time pattern
     * @return the formatted string
     */
    public static String formatDate(java.util.Date d, String pattern) {
        SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateInstance();

        formatter.applyPattern(pattern);
        return formatter.format(d);
    }

    public static int compareDate(java.util.Date src, java.util.Date desc) {
        if ((src == null) && (desc == null)) {
            return EQUAL;
        } else if (desc == null) {
            return BEFORE;
        } else if (src == null) {
            return AFTER;
        } else {
            long timeSrc = src.getTime();
            long timeDesc = desc.getTime();

            if (timeSrc == timeDesc) {
                return EQUAL;
            } else {
                return (timeDesc > timeSrc) ? AFTER
                        : BEFORE;
            }
        }
    }

    /**
     * (include time)
     *
     * @param first  date1
     * @param second date2
     * @return EQUAL  - if equal, BEFORE - if before than date2 , AFTER  - if over than date2
     */
    public static int compareTwoDate(Date first, Date second) {
        if ((first == null) && (second == null)) {
            return EQUAL;
        } else if (first == null) {
            return BEFORE;
        } else if (second == null) {
            return AFTER;
        } else if (first.before(second)) {
            return BEFORE;
        } else if (first.after(second)) {
            return AFTER;
        } else {
            return EQUAL;
        }
    }

    /**
     * @param date  the specified date
     * @param begin date1
     * @param end   date2
     * @return true  - between date1 and date2 false - not between date1 and date2
     */
    public static boolean isDateBetween(Date date, Date begin, Date end) {
        int c1 = compareTwoDate(begin, date);
        int c2 = compareTwoDate(date, end);

        return (((c1 == BEFORE) && (c2 == BEFORE)) || (c1 == EQUAL) || (c2 == EQUAL));
    }

    //use system current date as baseline date
    public static boolean isDateBetween(java.util.Date myDate, int begin, int end) {
        return isDateBetween(myDate, getCurrentDateTime(), begin, end);
    }

    public static boolean isDateBetween(java.util.Date utilDate, java.util.Date dateBaseLine,
                                        int begin, int end) {
        String pattern = TIME_PATTERN;

        String my = toString(utilDate, pattern);
        Date myDate = parseString2Date(my, pattern);

        String baseLine = toString(dateBaseLine, pattern);

        //		Date baseLineDate = parseString2Date(baseLine, pattern);
        String from = addDays(baseLine, begin);
        Date fromDate = parseString2Date(from, pattern);

        String to = addDays(baseLine, end);
        Date toDate = parseString2Date(to, pattern);

        return isDateBetween(myDate, fromDate, toDate);
    }

    /**
     * change string to Timestamp
     *
     * @param str  formatted timestamp string
     * @param sFmt string format
     * @return timestamp
     */
    public static Timestamp parseString2Timestamp(String str, String sFmt) {
        if ((str == null) || str.equals("")) {
            return null;
        }

        try {
            long time = Long.parseLong(str);

            return new Timestamp(time);
        } catch (Exception ex) {
            try {
                DateFormat df = new SimpleDateFormat(sFmt);
                java.util.Date dt = df.parse(str);

                return new Timestamp(dt.getTime());
            } catch (Exception pe) {
                try {
                    return Timestamp.valueOf(str);
                } catch (Exception e) {
                    return null;
                }
            }
        }
    }

    /**
     * parse a string into date  in a patter
     *
     * @param str  string
     * @param sFmt date pattern
     * @return date
     */
    public static Date parseString2Date(String str, String sFmt) {
        if ((str == null) || str.equals("")) {
            return null;
        }

        try {
            long time = Long.parseLong(str);

            return new Date(time);
        } catch (Exception ex) {
            try {
                DateFormat df = new SimpleDateFormat(sFmt);
                java.util.Date dt = df.parse(str);

                return new Date(dt.getTime());
            } catch (Exception pe) {
                try {
                    return Date.valueOf(str);
                } catch (Exception e) {
                    return null;
                }
            }
        }
    }

    /**
     * parse a string into date  in a patter
     *
     * @param str  string
     * @param sFmt date pattern
     * @return date
     */
    public static java.util.Date parseString3Date(String str, String sFmt) {
        if ((str == null) || str.equals("")) {
            return null;
        }

        try {
            long time = Long.parseLong(str);

            return new Date(time);
        } catch (Exception ex) {
            try {
                DateFormat df = new SimpleDateFormat(sFmt);
                java.util.Date dt = df.parse(str);

                return new Date(dt.getTime());
            } catch (Exception pe) {
                try {
                    return Date.valueOf(str);
                } catch (Exception e) {
                    return null;
                }
            }
        }
    }

    public static String addDays(java.util.Date date, int day, String pattern) {
        return addDays(toString(date, pattern), day, pattern);
    }

    public static String addDays(java.util.Date date, int day) {
        return addDays(toString(date, TIME_PATTERN), day);
    }

    public static String addDays(String date, int day) {
        return addDays(date, day, TIME_PATTERN);
    }

    /**
     * get the time of the specified date after given days
     *
     * @param date the specified date
     * @param day  day distance
     * @return the format string of time
     */
    public static String addDays(String date, int day, String pattern) {
        if (date == null) {
            return "";
        }

        if (date.equals("")) {
            return "";
        }

        if (day == 0) {
            return date;
        }

        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
            Calendar calendar = dateFormat.getCalendar();

            calendar.setTime(dateFormat.parse(date));
            calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) + day);
            return dateFormat.format(calendar.getTime());
        } catch (Exception ex) {
            ex.printStackTrace();
            return "";
        }
    }

    /**
     * change timestamp to formatted string
     *
     * @param t    Timestamp
     * @param sFmt date format
     * @return formatted string
     */
    public static String formatTimestamp(Timestamp t, String sFmt) {
        if (t == null) {
            return "";
        }

        t.setNanos(0);

        DateFormat ft = new SimpleDateFormat(sFmt);
        String str = "";

        try {
            str = ft.format(t);
        } catch (NullPointerException e) {

        }

        return str;
    }

    /**
     * change string to Timestamp
     *
     * @param str  formatted timestamp string
     * @param sFmt string format
     * @return timestamp
     */
    public static Timestamp parseString(String str, String sFmt) {
        if ((str == null) || str.equals("")) {
            return null;
        }

        try {
            long time = Long.parseLong(str);

            return new Timestamp(time);
        } catch (Exception ex) {
            try {
                DateFormat df = new SimpleDateFormat(sFmt);
                java.util.Date dt = df.parse(str);

                return new Timestamp(dt.getTime());
            } catch (Exception pe) {
                try {
                    return Timestamp.valueOf(str);
                } catch (Exception e) {
                    return null;
                }
            }
        }
    }

    /**
     * return current date
     *
     * @return current date
     */
    public static Date getCurrentDate() {
        return new Date(System.currentTimeMillis());
    }

    /**
     * return current time
     *
     * @return current time
     */
    public static Timestamp getCurrentDateTime() {
        return new Timestamp(System.currentTimeMillis());
    }

    /**
     * 计算时间差（天）
     */
    public static int getTimeIntervalDays(java.util.Date date, java.util.Date date2) {
        long intervals = date2.getTime() - date.getTime() + (60 * 1000);

        if (intervals > 0) {
            long daysd = intervals / (24 * 60 * 60 * 1000);
            daysd += (intervals % (24 * 60 * 60 * 1000) > 0 ? 1 : 0);
            return Long.valueOf(daysd).intValue();
        }

        return 0;
    }


    /**
     * 通过时间秒毫秒数判断两个时间的间隔
     *
     * @param date
     * @return
     */
    public static Integer differentDaysByMillisecond(java.util.Date date) {
        try {
            if (date == null) {
                return null;
            }
            Calendar calendar1 = Calendar.getInstance();
            calendar1.setTime(new java.util.Date(System.currentTimeMillis()));
            calendar1.set(Calendar.HOUR_OF_DAY, 0);
            calendar1.set(Calendar.MINUTE, 0);
            calendar1.set(Calendar.SECOND, 0);
            calendar1.set(Calendar.MILLISECOND, 0);

            Calendar calendar2 = Calendar.getInstance();
            calendar2.setTime(date);
            calendar2.set(Calendar.HOUR_OF_DAY, 0);
            calendar2.set(Calendar.MINUTE, 0);
            calendar2.set(Calendar.SECOND, 0);
            calendar2.set(Calendar.MILLISECOND, 0);
            int days;
            days = (int) ((calendar1.getTimeInMillis() - calendar2.getTimeInMillis()) / (1000 * 3600 * 24));
            return days;
        } catch (Exception e) {
            log.error("日期计算出错" + e.getMessage());
            return null;
        }
    }


    /**
     * 判断两个日期时间差
     *
     * @param date1
     * @param date2
     * @return
     */
    public static Integer differentDaysByMillisecond(java.util.Date date1, java.util.Date date2) {
        try {
            Calendar calendar1 = Calendar.getInstance();
            calendar1.setTime(date1);
            calendar1.set(Calendar.HOUR_OF_DAY, 0);
            calendar1.set(Calendar.MINUTE, 0);
            calendar1.set(Calendar.SECOND, 0);
            calendar1.set(Calendar.MILLISECOND, 0);

            Calendar calendar2 = Calendar.getInstance();
            calendar2.setTime(date2);
            calendar2.set(Calendar.HOUR_OF_DAY, 0);
            calendar2.set(Calendar.MINUTE, 0);
            calendar2.set(Calendar.SECOND, 0);
            calendar2.set(Calendar.MILLISECOND, 0);
            int days;
            days = (int) ((calendar1.getTimeInMillis() - calendar2.getTimeInMillis()) / (1000 * 3600 * 24));
            return days;
        } catch (Exception e) {
            log.error("日期计算出错");
            return null;
        }
    }


    /**
     * 判断当前日期和给定日期相差几年
     *
     * @param date
     * @return
     */
    public static int differectYear(java.util.Date date) {
        Calendar from = Calendar.getInstance();
        from.setTime(new java.util.Date());
        Calendar to = Calendar.getInstance();
        to.setTime(date);
        int fromYear = from.get(Calendar.YEAR);
        int toYear = to.get(Calendar.YEAR);
        return fromYear - toYear;
    }

    /**
     * 获取上n个小时整点小时时间
     *
     * @param date
     * @return
     */
    public static String getLastHourTime(java.util.Date date, int n) {
        Calendar ca = Calendar.getInstance();
        ca.set(Calendar.MINUTE, 0);
        ca.set(Calendar.SECOND, 0);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        ca.set(Calendar.HOUR_OF_DAY, ca.get(Calendar.HOUR_OF_DAY) - n);
        date = ca.getTime();
        return sdf.format(date);
    }

    /**
     * 获取当前时间的整点小时时间
     *
     * @param date
     * @return
     */
    public static String getCurrHourTime(java.util.Date date) {
        Calendar ca = Calendar.getInstance();
        ca.set(Calendar.MINUTE, 0);
        ca.set(Calendar.SECOND, 0);
        date = ca.getTime();
        SimpleDateFormat sdf = new SimpleDateFormat(TIME_PATTERN);
        return sdf.format(date);
    }

    /**
     * 获取前一天的时间
     *
     * @return
     */
    public static String getDayBefore() {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Calendar c = Calendar.getInstance();
        c.add(Calendar.DATE, -1);
        java.util.Date start = c.getTime();
        String qyt = format.format(start);//前一天
        return qyt;
    }

    /**
     * 计算现在到今晚还剩多少秒 库存盘点设置分布式锁失效时间用
     *
     * @return
     */
    public static long getSecondsNowToTonight() {
        Calendar calendar1 = Calendar.getInstance();
        java.util.Date date = new java.util.Date();
        calendar1.setTime(date);


        Calendar calendar2 = Calendar.getInstance();
        calendar2.setTime(date);
        calendar2.set(Calendar.HOUR_OF_DAY, 23);
        calendar2.set(Calendar.MINUTE, 59);
        calendar2.set(Calendar.SECOND, 59);
        return calendar2.getTimeInMillis() / 1000 - calendar1.getTimeInMillis() / 1000;

    }

    /**
     * 获取两个时间节点之间的月份列表
     **/
    public static List<String> getMonthBetween(String minDate, String maxDate) {
        //传入年份返回整年月份
        if (minDate.length() == 4) {
            minDate = minDate.concat("-01-01");
            maxDate = minDate.concat("-12-01");
        }
        ArrayList<String> result = new ArrayList<String>();
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DATE_FMT_5);//格式化为年月

            Calendar min = Calendar.getInstance();
            Calendar max = Calendar.getInstance();
            min.setTime(sdf.parse(minDate));
            min.set(min.get(Calendar.YEAR), min.get(Calendar.MONTH), 1);

            max.setTime(sdf.parse(maxDate));
            max.set(max.get(Calendar.YEAR), max.get(Calendar.MONTH), 2);

            Calendar curr = min;
            while (curr.before(max)) {
                result.add(sdf.format(curr.getTime()).concat("-01"));
                curr.add(Calendar.MONTH, 1);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return result;
    }


    public static String transDate(String str) {
        String end = str.substring(4);
        String start = str.substring(0, 4);
        return start + "-" + end;
    }

    //获取当前会计月
    public static String getAccountMonth(java.util.Date date) {
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        }
        int day = calendar.get(Calendar.DATE);
        int month = calendar.get(Calendar.MONTH);
        int year = calendar.get(Calendar.YEAR);
        if (day > 25 && month == 11) {
            ++year;
            month = 0;
        } else if (day > 25 && month < 11) {
            ++month;
        }
        StringBuilder builder = new StringBuilder();
        builder.append(year);
        builder.append("-");
        month = month + 1;
        if (month < 10) {
            builder.append("0");
        }
        builder.append(month);
        return builder.toString();
    }

    //获取当前会计月
    public static java.util.Date getAccountMonthTime(java.util.Date date) {
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        }
        int day = calendar.get(Calendar.DATE);
        int month = calendar.get(Calendar.MONTH);
        int year = calendar.get(Calendar.YEAR);
        if (day > 25 && month == 11) {
            ++year;
            month = 0;
        } else if (day > 25 && month < 11) {
            ++month;
        }
        calendar.set(year, month, day);
        return calendar.getTime();
    }

    /**
     * 获取指定时间前的时间
     *
     * @param num  天数
     * @param date 时间
     * @return
     */
    public static java.util.Date startTime(Integer num, java.util.Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, -num);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DATE),
                00, 00, 00);
        return calendar.getTime();
    }

    /**
     * 获取指定时间前的时间
     *
     * @param num  天数
     * @param date 时间
     * @return
     */
    public static java.util.Date endTime(Integer num, java.util.Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, -num);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DATE),
                23, 59, 59);
        return calendar.getTime();
    }

//    public static void main(String[] args) {
//        Calendar calendar = Calendar.getInstance();
////        calendar.set(2019,10,26);
//
//        System.out.println(getAccountMonth(calendar.getTime()));
//    }


    /**
     * 获取当前会计月额开始时间或结束时间
     * @param date
     * @param boo true 开始时间，false结束时间
     * @return
     */
    public static long getAccountMonthTimeInMillis(java.util.Date date,boolean boo) {
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        }
        int day = calendar.get(Calendar.DATE);
        int month = calendar.get(Calendar.MONTH);
        int year = calendar.get(Calendar.YEAR);
        if(boo){
            if (day < 25 && month == 0){
                month = 11;
                --year;
            }
            calendar.set(year,month,26,0,0,0);
            return calendar.getTimeInMillis();
        }
        if (day > 25 && month == 11) {
            ++year;
            month = 0;
        } else if (day > 25 && month < 11) {
            ++month;
        }
        calendar.set(year, month, 25,23,59,59);
        return calendar.getTimeInMillis();
    }
}
